package com.term.util.converter;

import com.term.domain.BaseDomain;
import com.term.util.enums.EnumUtils;
import com.term.util.locator.BeanLocator;
import com.term.util.string.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;

/**
 * Created by chenGang on 2017/1/11.
 */
public class ClassUtils {
    /**
     * 获取类clazz的所有Field，包括其父类的Field，如果重名，以子类Field为准。
     *
     * @param clazz
     * @return Field数组
     */
    public static Field[] getAllFieldsWithRoot(Class<?> clazz) {
        return getAllFieldsWithRoot(clazz, null);
    }


    /**
     * 获取类clazz的所有Field，包括其父类的Field，如果重名，以子类Field为准。无静态属性
     *
     * @param clazz
     * @return Field数组
     */
    public static Field[] getAllFieldsWithRootNoStatic(Class<?> clazz) {
        return getAllFieldsWithRoot(clazz, item -> !Modifier.isStatic(item.getModifiers()));
    }



    /**
     * 获取类clazz的所有Field，包括其父类的Field，如果重名，以子类Field为准。
     *
     * @param clazz
     * @return Field数组
     */
    public static Field[] getAllFieldsWithRoot(Class<?> clazz, Predicate<Field> predicate) {
        List<Field> fieldList = new ArrayList<Field>();
        Field[] dFields = clazz.getDeclaredFields();
        if (null != dFields && dFields.length > 0) fieldList.addAll(Arrays.asList(dFields));

        // 若父类是Object，则直接返回当前Field列表
        Class<?> superClass = clazz.getSuperclass();
        if (superClass == Object.class) return dFields;

        // 递归查询父类的field列表
        Field[] superFields = getAllFieldsWithRoot(superClass);
        if (null != superFields && superFields.length > 0) {
            Stream.of(superFields).filter(field -> !fieldList.contains(field))
                    .filter(field -> predicate == null || predicate.test(field))
                    .forEach(field -> fieldList.add(field));
        }

        Field[] result = new Field[fieldList.size()];
        fieldList.toArray(result);
        return result;
    }

    /**
     * 根据field名字和目标class获取field字段
     *
     * @param clazz
     * @param name
     * @return
     */
    public static Field getFieldWithRoot(Class<?> clazz, String name) {
        Field[] dFields = getAllFieldsWithRoot(clazz);
        return Stream.of(dFields).filter(field -> field.getName().equals(name)).findFirst().orElse(null);
    }


    /**
     * 根据请求对象和属性的name来用get方法获取对应的属性值
     *
     * @param obj
     * @param name
     * @return
     */
    public static Object getValue(Object obj, String name) {
        Object value = null;
        try {
            Method method = obj.getClass().getMethod("get" + name.substring(0, 1).toUpperCase() + name.substring(1, name
                    .length()));
            value = method.invoke(obj);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return value;
    }


    /**
     * 把找到的具体值设置到目标对象中
     *
     * @param name
     * @param value
     * @param targetObj
     */
    public static void setValue(String name, Object value, Object targetObj) {
        try {
            Class fieldClass = getFieldWithRoot(targetObj.getClass(), name).getType();
            Method method = targetObj.getClass().getMethod("set" + StringUtils.toFirstUpCase(name), fieldClass);
            // 从数据库转换到实体的类时候，有可能出现传入的value和目标的实体类的属性类型不匹配的情况，导致报错java.lang.IllegalArgumentException: argument type mismatch
            // 这里把传入的value转换为对应的实体类的属性的类型
            value = processValue(value, fieldClass, targetObj);
            method.invoke(targetObj, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 当value和fieldClass类型不匹配时，进行转换，把value转化为fieldClass
     * 当前遇到的情况数据库传来的value类型为bigInteger，目标属性的值为Long
     *
     * @param value
     * @param fieldClass
     * @param targetObj
     * @return
     */
    private static Object processValue(Object value, Class fieldClass, Object targetObj) {

        // 若值为null，则直接返回，不需要转换
        if (value == null) return value;

        // 若数据库是字符串类型，而目标是枚举类型，则根据枚举的name进行转化
        if (fieldClass.isEnum()) {
            return EnumUtils.getEnumByEnumName(fieldClass, value);
        } else {
            IConverter converter = (IConverter) BeanLocator.findBeanByName(value.getClass().getSimpleName() + "_" + fieldClass.getSimpleName() +
                    "_SET_VALUE");
            if (converter == null) return value;
            return converter.convert(value, fieldClass, targetObj);
        }
    }

}
